import React, { useEffect, useRef, useState, useMemo } from 'react';
import { View, Text } from '@tarojs/components';
import dayjs from 'dayjs';
import './index.scss';

type Props = {
  initialDate?: string | Date;
  onChange?: (d: string) => void;
  onMonthChange?: (d: string) => void;
  maxOffsetMonths?: number;
  style?: React.CSSProperties;
  isExpanded?: boolean;
  dayMap?: {
    date: string;
    count: number;
  }[];
};

const ROW_HEIGHT = 40; // 与 scss 中保持一致（px）
const SWIPE_THRESHOLD = 50; // 触发月切换的滑动阈值（px）
const ANIM_DURATION = 300; // 切换动画时长 ms

function makeWeeksForMonth(month: dayjs.Dayjs) {
  const start = month.startOf('month').startOf('week'); // 周日开始
  const end = month.endOf('month').endOf('week');
  const days: dayjs.Dayjs[] = [];
  let d = start;
  while (d.isBefore(end) || d.isSame(end)) {
    days.push(d);
    d = d.add(1, 'day');
  }
  const weeks: dayjs.Dayjs[][] = [];
  for (let i = 0; i < days.length; i += 7) weeks.push(days.slice(i, i + 7));
  return weeks;
}

const CollapsibleCalendar: React.FC<Props> = ({
  initialDate,
  onChange,
  onMonthChange,
  maxOffsetMonths = 5,
  style,
  isExpanded = false,
  dayMap,
}) => {
  const today = dayjs();
  const init = initialDate ? dayjs(initialDate) : today;

  const [currentMonth, setCurrentMonth] = useState(
    dayjs(init).startOf('month'),
  );
  const [selectedDate, setSelectedDate] = useState(dayjs(init));
  const [expanded, setExpanded] = useState(isExpanded);

  // 三面板数据
  const [prevWeeks, setPrevWeeks] = useState<dayjs.Dayjs[][]>([]);
  const [curWeeks, setCurWeeks] = useState<dayjs.Dayjs[][]>([]);
  const [nextWeeks, setNextWeeks] = useState<dayjs.Dayjs[][]>([]);

  const [panelHeight, setPanelHeight] = useState(ROW_HEIGHT); // 整个 panel 的像素高度（expanded 时为 rows*ROW_HEIGHT）
  const [translateY, setTranslateY] = useState(-ROW_HEIGHT); // slider 的 translateY，初始把中间面板置中
  const [isAnimating, setIsAnimating] = useState(false);

  const startY = useRef(0);
  const deltaY = useRef(0);
  const moving = useRef(false);
  const animTimer = useRef<number | null>(null);

  const minMonth = today.subtract(maxOffsetMonths, 'month').startOf('month');
  const maxMonth = today.add(maxOffsetMonths, 'month').startOf('month');

  // 根据 dayMap 构建日期到数量的映射，便于渲染小点
  const dayCountMap = useMemo(() => {
    const result: Record<string, number> = {};
    (dayMap || []).forEach(({ date, count }) => {
      const key = dayjs(date).format('YYYY-MM-DD');
      result[key] = count;
    });
    return result;
  }, [dayMap]);

  // 生成当前月份周数据（prev/cur/next），并计算 panelHeight
  const regenPanels = (month: dayjs.Dayjs) => {
    const cur = month.startOf('month');
    const prev = cur.subtract(1, 'month');
    const next = cur.add(1, 'month');
    const wPrev = makeWeeksForMonth(prev);
    const wCur = makeWeeksForMonth(cur);
    const wNext = makeWeeksForMonth(next);

    setPrevWeeks(wPrev);
    setCurWeeks(wCur);
    setNextWeeks(wNext);

    // const maxRows = Math.max(wPrev.length, wCur.length, wNext.length);

    const newPanelHeight = expanded ? wCur.length * ROW_HEIGHT : ROW_HEIGHT;
    setPanelHeight(newPanelHeight);
    // 中间面板居中
    setTranslateY(-newPanelHeight);
  };

  // 初始化 & currentMonth/expanded 改变时更新
  useEffect(() => {
    regenPanels(currentMonth);
  }, [currentMonth, expanded]);

  // selectedDate -> onChange 回调
  useEffect(() => {
    onChange?.(dayjs(selectedDate).format('YYYY-MM-DD'));
  }, [selectedDate]);

  // selectedDate -> onChange 回调
  useEffect(() => {
    onMonthChange?.(dayjs(currentMonth).format('YYYY-MM'));
  }, [currentMonth]);

  useEffect(() => {
    setExpanded(isExpanded);
  }, [isExpanded]);

  // 手势处理（兼容 clientY 或 pageY 两种属性）
  const getTouchY = (t: any) =>
    (t && (typeof t.clientY !== 'undefined' ? t.clientY : t.pageY)) ?? 0;

  const handleTouchStart = (e: any) => {
    if (isAnimating) return;
    const t = e.touches && e.touches[0];
    startY.current = getTouchY(t);
    deltaY.current = 0;
    moving.current = true;
    if (animTimer.current) {
      clearTimeout(animTimer.current);
      animTimer.current = null;
    }
  };

  const handleTouchMove = (e: any) => {
    if (!moving.current || isAnimating) return;
    const t = e.touches && e.touches[0];
    if (!t) return;
    deltaY.current = getTouchY(t) - startY.current;
    let next = -panelHeight + deltaY.current;
    const maxTranslate = 0;
    const minTranslate = -2 * panelHeight;
    if (next > maxTranslate) next = maxTranslate;
    if (next < minTranslate) next = minTranslate;
    setTranslateY(next);
  };

  // 完成切换：target 'next' | 'prev' | 'stay'
  const finishSwipeTo = (target: 'prev' | 'next' | 'stay') => {
    if (animTimer.current) {
      clearTimeout(animTimer.current);
      animTimer.current = null;
    }

    if (target === 'next') {
      // 动画到下一个面板（向上滑）
      setIsAnimating(true);
      setTranslateY(-2 * panelHeight); // 动画：移到最下（显示 next）
      animTimer.current = window.setTimeout(() => {
        // 计算 new month 及其 panels，避免渲染跳动
        const nextMonth = currentMonth.add(1, 'month').startOf('month');
        const wPrevNew = makeWeeksForMonth(nextMonth.subtract(1, 'month'));
        const wCurNew = makeWeeksForMonth(nextMonth);
        const wNextNew = makeWeeksForMonth(nextMonth.add(1, 'month'));
        const newMax = Math.max(
          wPrevNew.length,
          wCurNew.length,
          wNextNew.length,
        );
        const newPH = expanded ? newMax * ROW_HEIGHT : ROW_HEIGHT;

        // 立即写入新 panels 与高度（这样不会触发额外动画）
        setPrevWeeks(wPrevNew);
        setCurWeeks(wCurNew);
        setNextWeeks(wNextNew);
        setPanelHeight(newPH);

        // 取消动画，让 translateY 直接跳回中间（避免闪烁）
        setIsAnimating(false);
        setTranslateY(-newPH);

        // 最后同步 currentMonth 状态
        setCurrentMonth(nextMonth);
      }, ANIM_DURATION);
    } else if (target === 'prev') {
      setIsAnimating(true);
      setTranslateY(0); // 动画到上一个面板（显示 prev）
      animTimer.current = window.setTimeout(() => {
        const prevMonth = currentMonth.subtract(1, 'month').startOf('month');
        const wPrevNew = makeWeeksForMonth(prevMonth.subtract(1, 'month'));
        const wCurNew = makeWeeksForMonth(prevMonth);
        const wNextNew = makeWeeksForMonth(prevMonth.add(1, 'month'));
        const newMax = Math.max(
          wPrevNew.length,
          wCurNew.length,
          wNextNew.length,
        );
        const newPH = expanded ? newMax * ROW_HEIGHT : ROW_HEIGHT;

        setPrevWeeks(wPrevNew);
        setCurWeeks(wCurNew);
        setNextWeeks(wNextNew);
        setPanelHeight(newPH);

        setIsAnimating(false);
        setTranslateY(-newPH);
        setCurrentMonth(prevMonth);
      }, ANIM_DURATION);
    } else {
      // 回弹到中间
      setIsAnimating(true);
      setTranslateY(-panelHeight);
      animTimer.current = window.setTimeout(() => {
        setIsAnimating(false);
      }, ANIM_DURATION);
    }
  };

  const handleTouchEnd = () => {
    if (!moving.current) return;
    moving.current = false;
    if (deltaY.current < -SWIPE_THRESHOLD) {
      // 上滑 -> 下一个月
      if (currentMonth.add(1, 'month').startOf('month').isAfter(maxMonth)) {
        finishSwipeTo('stay');
      } else finishSwipeTo('next');
    } else if (deltaY.current > SWIPE_THRESHOLD) {
      // 下滑 -> 上一个月
      if (
        currentMonth.subtract(1, 'month').startOf('month').isBefore(minMonth)
      ) {
        finishSwipeTo('stay');
      } else finishSwipeTo('prev');
    } else {
      finishSwipeTo('stay');
    }
  };

  // 点击选择日期（selected 为 outline，today 为 filled）
  const handleSelectDay = (d: dayjs.Dayjs) => {
    setSelectedDate(d);
    // 如果点击不在当前 month，则切到对应 month（这样 collapse 时能显示对应行）
    if (!d.isSame(currentMonth, 'month')) {
      setCurrentMonth(d.startOf('month'));
    }
  };

  // header 按钮也使用滑动动画（体验统一）
  const headerChange = (dir: 'prev' | 'next') => {
    if (dir === 'prev') {
      if (currentMonth.subtract(1, 'month').isBefore(minMonth)) return;
      // 伪造一次下滑（prev）动画
      finishSwipeTo('prev');
    } else {
      if (currentMonth.add(1, 'month').isAfter(maxMonth)) return;
      finishSwipeTo('next');
    }
  };

  // 渲染一周（保证每行高度固定）
  const renderWeek = (
    week: dayjs.Dayjs[],
    idx: number,
    monthRef: dayjs.Dayjs,
  ) => (
    <View
      className="calendar-week"
      key={idx}
      style={{ height: `${ROW_HEIGHT}px` }}
    >
      {week.map((d) => {
        const isToday = d.isSame(dayjs(), 'day');
        const isSelected = d.isSame(selectedDate, 'day');
        // 优先显示 today 的 filled 样式，否则如果 selected 则 outline
        const cls = isToday ? 'today' : isSelected ? 'selected' : '';
        const notCurrent = !d.isSame(monthRef, 'month');
        return (
          <View
            key={d.toString()}
            className={`calendar-day ${cls} ${notCurrent ? 'not-current' : ''}`}
            onClick={() => handleSelectDay(d)}
          >
            <Text className="date-number">{d.date()}</Text>
            {(() => {
              if (isToday || isSelected) return null;
              const dateKey = d.format('YYYY-MM-DD');
              const count = Number(dayCountMap[dateKey]) || 0;
              const dots = Math.max(0, Math.min(6, count));
              return dots > 0 ? (
                <View className="dots">
                  {Array.from({ length: dots }).map((_, i) => (
                    <View key={i} className="dot orange" />
                  ))}
                </View>
              ) : null;
            })()}
          </View>
        );
      })}
    </View>
  );

  const renderPanel = (weeksArr: dayjs.Dayjs[][], monthRef: dayjs.Dayjs) => (
    <View className="panel-inner" style={{ height: `${panelHeight}px` }}>
      {expanded
        ? weeksArr.map((w, i) => renderWeek(w, i, monthRef))
        : (() => {
            const findIndex = weeksArr.findIndex((w) =>
              w.some((d) => d.isSame(selectedDate, 'day')),
            );
            const idxToShow = findIndex >= 0 ? findIndex : 0;
            return renderWeek(weeksArr[idxToShow], idxToShow, monthRef);
          })()}
    </View>
  );

  return (
    <View className="collapsible-calendar" style={style}>
      <View className="cal-header">
        <Text
          className={`arrow ${currentMonth.isSame(minMonth) ? 'disabled' : ''}`}
          onClick={() => headerChange('prev')}
        >
          ‹
        </Text>
        <Text className="title">{currentMonth.format('YYYY年M月')}</Text>
        <Text
          className={`arrow ${currentMonth.isSame(maxMonth) ? 'disabled' : ''}`}
          onClick={() => headerChange('next')}
        >
          ›
        </Text>
      </View>

      <View className="weekdays">
        {['日', '一', '二', '三', '四', '五', '六'].map((w) => (
          <Text className="weekday" key={w}>
            {w}
          </Text>
        ))}
      </View>

      <View
        className="slider-viewport"
        style={{ height: `${panelHeight}px` }}
        onTouchStart={handleTouchStart}
        onTouchMove={handleTouchMove}
        onTouchEnd={handleTouchEnd}
      >
        <View
          className="slider"
          style={{
            height: `${panelHeight * 3}px`,
            transform: `translateY(${translateY}px)`,
            transition: isAnimating
              ? `transform ${ANIM_DURATION}ms ease`
              : 'none',
          }}
        >
          <View className="panel">
            {prevWeeks.length
              ? renderPanel(prevWeeks, currentMonth.subtract(1, 'month'))
              : null}
          </View>
          <View className="panel">
            {curWeeks.length ? renderPanel(curWeeks, currentMonth) : null}
          </View>
          <View className="panel">
            {nextWeeks.length
              ? renderPanel(nextWeeks, currentMonth.add(1, 'month'))
              : null}
          </View>
        </View>
      </View>

      <View className="toggle" onClick={() => setExpanded((s) => !s)}>
        <Text className="toggle-icon">{expanded ? '︿' : '﹀'}</Text>
      </View>
    </View>
  );
};

export default CollapsibleCalendar;
